Cloudflare Worker で Google IAP の jwt ペイロードを検証する
GCP 側で設定して Cloudflare Worker にリクエストを流すようにすると request の headers に x-goog-iap-jwt-assertion として jwt のペイロードが含まって送られてくる Node.js のコードでは google-auth-library に実際の検証コードがあるので node コンパチ環境ではこれを使うでいけるのだが、Worker には当然 WebCrypto 相当の API しか生えてないし、fs とかもないのでそのまま使うことはできない 仕方ないので google-auth-library を読み下してそれっぽいコードを実装した
JWT はヘッダとペイロードとシグネチャの3セグあるかをバリデートする
ヘッダとペイロードは JSON.parse(atob(seg[0])) みたいな感じでデコードする
ヘッダに kid が入ってるので、jwk の keys から該当する cert を探してくる
無知なのでアレだけど、importKey は { name: 'ECDSA', namedCurve: 'P-256' }、verify は { name: 'ECDSA', hash: { name: 'SHA-256' } } をそれぞれ指定する
importKey は何もしなくても jwk で取得できるので問題ないが、verify の方は引数が ArrayBuffer なので base64エンコード の文字列から起こすのが地味に面倒くさい とりあえずこれで検証自体はできるので、あとは iat と exp と iss と aud をそれぞれ検証すればよい
worker => worker は service bindings で呼び出すにして、workers.dev を塞いでやれば IAP を通したリクエストだけが子 worker にやってくる状態がつくれる worker => pages は service bindigs ではつながらないので、pages.dev を fetch でつないでやるなどしないといけない
ここのリクエストに事前共有の値をヘッダにくっつけてリクエストを送って、リクエストを受け取る pages function でリクエストのヘッダを検証して、ヘッダがついていれば200を返して、なければ401とすることで擬似的な制限が可能になる
ただ、pages function を通らないアセット類に関してはガバガバなのでフルオープンになっちゃう
Access だとアセット類もちゃんと塞いでくれるので偉い
とはいえ現代のツールチェインを使ってるとだいたいアセットにはダイジェストがくっついてるので、攻撃したい人がドンズバで URL 当てないと引っ張ってこれないのでまあ妥協ラインとしてはそんなもんかなあと
あと worker => pages でつなぐ時は POST の body が ReadableStream として入ってきてはいるけど、pages の fetch の body としては使えないってことになってるっぽくて、一旦 body を worker の中で json なり formData なり確定させないといけない この時、formData だと headers の Content-Type に boundary が入ってて、headers をそのままパスしてると pages 側で formData として受け取れないので取り除いてやらないといけなかった